package com.soulkun.smarthome.logic.repository

import androidx.lifecycle.*
import com.google.gson.Gson
import com.soulkun.smarthome.logic.creator.ApiServiceCreator
import com.soulkun.smarthome.logic.dto.*
import com.soulkun.smarthome.logic.model.*
import com.soulkun.smarthome.logic.utils.ExceptionUtils
import kotlinx.coroutines.*
import okhttp3.MediaType
import okhttp3.RequestBody
import kotlin.coroutines.CoroutineContext

object ApiRepository {

    /**
     * ViewModel扩展方法：核心方法
     * 一般用于与LiveData绑定或者ViewModel生命周期内的协程调度
     * @param context 指定协程上下文，除CoroutineExceptionHandler外，内部已指定
     * @param dataBlock 协程逻辑
     * @param onError 错误回调方法
     * @param onComplete 完成回调方法
     */
    private fun <T> ViewModel.apiLaunch(
        context: CoroutineContext,
        dataBlock: suspend CoroutineScope.() -> T,
        onError: (e: Throwable) -> Unit = { _: Throwable -> },
        onComplete: suspend CoroutineScope.(T) -> Unit = {}
    ) {
        viewModelScope.launch(
            context +
                    CoroutineExceptionHandler { _, throwable ->
                        run {
                            // 这里统一处理错误
                            onError(throwable)
                        }
                    }
        ) {
            val result = dataBlock.invoke(this)
            withContext(Dispatchers.Main) {
                onComplete.invoke(this, result)
            }
        }
    }


    /**
     * 利用viewModelScope快速执行协程
     * @param dataBlock 协程逻辑
     */
    private fun <T> ViewModel.apiLaunchQuick(
        dataBlock: suspend CoroutineScope.() -> T,
        onComplete: (T) -> Unit
    ) =
        apiLaunch<T>(Dispatchers.IO, dataBlock, onError = {
            GlobalScope.launch(Dispatchers.Main) {
                ExceptionUtils.catchException(it)
            }
        }, { result ->
            withContext(Dispatchers.Main) {
                onComplete(result)
            }
        })

    /**
     * 升级版
     * 利用viewModelScope快速执行协程，允许自定义完成回调方法的协程作用域
     * @param dataBlock 协程逻辑
     */
    private fun <T> ViewModel.apiLaunchQuickAdvanced(
        dataBlock: suspend CoroutineScope.() -> T,
        onComplete: suspend CoroutineScope.(T) -> Unit
    ) =
        apiLaunch<T>(Dispatchers.IO, dataBlock, onError = {
            GlobalScope.launch(Dispatchers.Main) {
                ExceptionUtils.catchException(it)
            }
        }, onComplete)

    /**
     * LiveDataScope方法：核心方法
     * @param context 协程上下文
     * @param dataBlock 数据逻辑
     * @param onError 错误回调方法
     */
    private fun <T> apiLaunch(
        context: CoroutineContext,
        dataBlock: suspend LiveDataScope<T>.() -> T,
        onError: (e: Throwable) -> Unit = { _: Throwable -> }
    ): LiveData<Result<T>> = liveData(context) {
        emit(
            try {
                val data = dataBlock()
                Result.success(data)
            } catch (e: Throwable) {
                onError(e)
                Result.failure(e)
            }
        )
    }

    /**
     * 利用LiveDataScope快速执行协程
     * @param dataBlock 数据逻辑
     */
    private fun <T> apiLaunchQuick(dataBlock: suspend LiveDataScope<T>.() -> T) =
        apiLaunch(Dispatchers.IO, dataBlock) {
            GlobalScope.launch(Dispatchers.Main) {
                ExceptionUtils.catchException(it)
            }
        }

    private fun <T> buildRequestBody(data: T): RequestBody {
        val gson = Gson()
        val toJson =
            gson.toJson(data)
        return RequestBody.create(MediaType.parse("application/json; charset=utf-8"), toJson)
    }

    /**
     * 用户方面的逻辑
     */
    fun login(username: String, password: String) = apiLaunchQuick<ResponseModel<LoginModel>> {
        ApiServiceCreator.login(
            UserModel(
                userName = username,
                password = password
            )
        )
    }

    fun register(username: String, password: String) = apiLaunchQuick<ResponseModel<Any>> {
        ApiServiceCreator.register(
            UserModel(
                userName = username,
                password = password
            )
        )
    }

    fun logout() = apiLaunchQuick<ResponseModel<Any>> {
        ApiServiceCreator.logout()
    }

    fun getUserInfo(viewModel: ViewModel, onComplete: (ResponseModel<UserModel>) -> Unit) =
        viewModel.apiLaunchQuick(
            { ApiServiceCreator.getUserInfo() }, onComplete
        )

    fun fillUserInfo(nickName: String) = apiLaunchQuick<ResponseModel<Any>> {
        ApiServiceCreator.fillUserInfo(UserModel(nickName = nickName))
    }

    fun checkAvailableUserName(username: String) = apiLaunchQuick<ResponseModel<Any>> {
        ApiServiceCreator.checkAvailableUserName(username)
    }

    fun getFamilyRoleList() = apiLaunchQuick<ResponseModel<List<RoleModel>>> {
        ApiServiceCreator.getFamilyRoleList()
    }

    /**
     * 家庭方面的逻辑
     */
    fun createFamily(familyRoomDTO: FamilyRoomDTO) = apiLaunchQuick<ResponseModel<Any>> {
        ApiServiceCreator.createFamily(familyRoomDTO)
    }

    fun generateFamilyInvitation(familyModel: FamilyModel) =
        apiLaunchQuick<ResponseModel<FamilyInvitationModel>> {
            ApiServiceCreator.generateFamilyInvitation(familyModel)
        }

    fun getFamilyDetailInfo(
        viewModel: ViewModel,
        familyModel: FamilyModel,
        onComplete: (ResponseModel<FamilyDetailInfoDTO>) -> Unit
    ) =
        viewModel.apiLaunchQuick({
            ApiServiceCreator.getFamilyDetailInfo(familyModel)
        }, onComplete)

    fun modifyFamilyName(
        viewModel: ViewModel,
        familyModel: FamilyModel,
        onComplete: (ResponseModel<Any>) -> Unit
    ) = viewModel.apiLaunchQuick<ResponseModel<Any>>({
        ApiServiceCreator.modifyFamilyName(familyModel)
    }, onComplete)

    fun modifyFamilyLocation(
        familyModel: FamilyModel
    ) = apiLaunchQuick<ResponseModel<Any>> {
        ApiServiceCreator.modifyFamilyLocation(familyModel)
    }

    fun joinFamily(familyInvitationDTO: FamilyInvitationDTO) = apiLaunchQuick<ResponseModel<Any>> {
        ApiServiceCreator.joinFamily(familyInvitationDTO)
    }

    fun getFamilyNameList(
        viewModel: ViewModel,
        onComplete: (ResponseModel<List<FamilyModel>>) -> Unit
    ) =
        viewModel.apiLaunchQuick<ResponseModel<List<FamilyModel>>>(
            { ApiServiceCreator.getFamilyNameList() }, onComplete
        )

    fun getRoomDeviceList(
        viewModel: ViewModel,
        familyModel: FamilyModel,
        onComplete: (ResponseModel<List<RoomDeviceDTO>>) -> Unit
    ) = viewModel.apiLaunchQuick<ResponseModel<List<RoomDeviceDTO>>>({
        ApiServiceCreator.getRoomDeviceList(familyModel)
    }, onComplete)

    fun deleteFamily(familyModel: FamilyModel) = apiLaunchQuick<ResponseModel<Any>> {
        ApiServiceCreator.deleteFamily(familyModel)
    }

    fun addRoom(
        viewModel: ViewModel,
        roomModel: RoomModel,
        onComplete: (ResponseModel<RoomModel>) -> Unit
    ) = viewModel.apiLaunchQuick<ResponseModel<RoomModel>>({
        ApiServiceCreator.addRoom(roomModel)
    }, onComplete)

    fun deleteRoom(
        viewModel: ViewModel,
        roomModel: RoomModel,
        onComplete: (ResponseModel<Any>) -> Unit
    ) =
        viewModel.apiLaunchQuick<ResponseModel<Any>>({
            ApiServiceCreator.deleteRoom(roomModel)
        }, onComplete)

    fun modifyRoomName(
        viewModel: ViewModel,
        roomModel: RoomModel,
        onComplete: (ResponseModel<Any>) -> Unit
    ) = viewModel.apiLaunchQuick<ResponseModel<Any>>({
        ApiServiceCreator.modifyRoomName(roomModel)
    }, onComplete)

    fun setFamilyUserRole(familyUserRoleDTO: FamilyUserRoleDTO) =
        apiLaunchQuick<ResponseModel<Any>> {
            ApiServiceCreator.setFamilyUserRole(familyUserRoleDTO)
        }

    fun deleteFamilyUser(familyUserRoleDTO: FamilyUserRoleDTO) =
        apiLaunchQuick<ResponseModel<Any>> {
            ApiServiceCreator.deleteFamilyUser(familyUserRoleDTO)
        }

    fun getAlertMessageList(
        viewModel: ViewModel,
        onComplete: suspend CoroutineScope.(ResponseModel<List<AlertMessageDTO>>) -> Unit
    ) = viewModel.apiLaunchQuickAdvanced({
        ApiServiceCreator.getAlertMessageList()
    }, onComplete)

    fun getFamilyMessageList(
        viewModel: ViewModel,
        onComplete: suspend CoroutineScope.(ResponseModel<List<FamilyMessageDTO>>) -> Unit
    ) = viewModel.apiLaunchQuickAdvanced(
        {
            ApiServiceCreator.getFamilyMessageList()
        }, onComplete
    )

    fun getRoomDeviceCommandList(
        viewModel: ViewModel,
        familyModel: FamilyModel,
        onComplete: (ResponseModel<List<RoomDeviceCommandDTO>>) -> Unit
    ) = viewModel.apiLaunchQuick({
        ApiServiceCreator.getRoomDeviceCommandList(familyModel)
    }, onComplete)

    /**
     * 房间方面的逻辑
     */
    fun addDevice(deviceModel: DeviceModel) = apiLaunchQuick<ResponseModel<Any>> {
        ApiServiceCreator.addDevice(deviceModel)
    }

    fun deleteDevice(deviceModel: DeviceModel) = apiLaunchQuick<ResponseModel<Any>> {
        ApiServiceCreator.deleteDevice(deviceModel)
    }

    fun modifyDeviceName(
        viewModel: ViewModel,
        deviceModel: DeviceModel,
        onComplete: (ResponseModel<Any>) -> Unit
    ) = viewModel.apiLaunchQuick<ResponseModel<Any>>({
        ApiServiceCreator.modifyDeviceName(deviceModel)
    }, onComplete)

    /**
     * 设备方面的逻辑
     */
    fun getDeviceData(
        viewModel: ViewModel,
        deviceModel: DeviceModel,
        onComplete: (ResponseModel<List<DeviceDataDTO>>) -> Unit
    ) = viewModel.apiLaunchQuick<ResponseModel<List<DeviceDataDTO>>>({
        ApiServiceCreator.getDeviceData(deviceModel)
    }, onComplete)

    fun getDeviceAttributeList(
        viewModel: ViewModel,
        deviceModel: DeviceModel,
        onComplete: (ResponseModel<List<AttributeModel>>) -> Unit
    ) = viewModel.apiLaunchQuick({
        ApiServiceCreator.getDeviceAttributeList(deviceModel)
    }, onComplete)

    fun getDeviceCommandList(
        deviceModel: DeviceModel,
    ) = apiLaunchQuick<ResponseModel<List<CommandDTO>>> {
        ApiServiceCreator.getDeviceCommandList(deviceModel)
    }

    fun postDeviceCommand(deviceCommandPostDTO: DeviceCommandPostDTO) =
        apiLaunchQuick<ResponseModel<Any>> {
            ApiServiceCreator.postDeviceCommand(deviceCommandPostDTO)
        }

    fun getDeviceDataHistory(
        viewModel: ViewModel,
        deviceDataHistoryRequestDTO: DeviceDataHistoryRequestDTO,
        onComplete: (ResponseModel<DeviceDataHistoryDTO>) -> Unit
    ) = viewModel.apiLaunchQuick({
        ApiServiceCreator.getDeviceDataHistory(deviceDataHistoryRequestDTO)
    }, onComplete)

    fun getDeviceCommandHistory(
        viewModel: ViewModel,
        deviceCommandHistoryRequestDTO: DeviceCommandHistoryRequestDTO,
        onComplete: (ResponseModel<DeviceCommandHistoryDTO>) -> Unit
    ) = viewModel.apiLaunchQuick({
        ApiServiceCreator.getDeviceCommandHistory(deviceCommandHistoryRequestDTO)
    }, onComplete)

}